--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 388fb78969e8bd23d19085f5acd575bf37133e6c
Parents : 7c0d536
Author : Ivan <ivan@quad4.io>
Signature : Invalid signer <e46112d44649266d71fe2193e00a4710>, author is <ivan@quad4.io>
Date : 2026-06-29T06:12:20-05:00
feat(tests): add API client usage guard test to prevent raw fetch usage for mutating API requests
Changes
Diff
diff --git a/tests/frontend/apiFetchGuard.test.js b/tests/frontend/apiFetchGuard.test.js
new file mode 100644
index 00000000..4b01d670
--- /dev/null
+++ b/tests/frontend/apiFetchGuard.test.js
@@ -0,0 +1,46 @@
+import { readFileSync, readdirSync, statSync } from "fs";
+import { join } from "path";
+import { describe, it, expect } from "vitest";
+
+const FRONTEND_ROOT = join(process.cwd(), "meshchatx/src/frontend");
+const SKIP_DIRS = new Set(["public", "node_modules"]);
+const SOURCE_EXT = /\.(vue|js)$/;
+
+function collectSourceFiles(dir, acc = []) {
+ for (const name of readdirSync(dir)) {
+ const path = join(dir, name);
+ const stat = statSync(path);
+ if (stat.isDirectory()) {
+ if (SKIP_DIRS.has(name)) {
+ continue;
+ }
+ collectSourceFiles(path, acc);
+ } else if (SOURCE_EXT.test(name)) {
+ acc.push(path);
+ }
+ }
+ return acc;
+}
+
+function relativePath(path) {
+ return path.replace(`${process.cwd()}/`, "");
+}
+
+describe("API client usage guard", () => {
+ it("does not use raw fetch for mutating /api/v1 requests", () => {
+ const offenders = [];
+ for (const file of collectSourceFiles(FRONTEND_ROOT)) {
+ if (file.endsWith("/js/apiClient.js")) {
+ continue;
+ }
+ const src = readFileSync(file, "utf8");
+ if (!src.includes("/api/v1") || !src.includes("fetch(")) {
+ continue;
+ }
+ if (/fetch\s*\([\s\S]{0,400}?\/api\/v1[\s\S]{0,400}?method\s*:\s*["'](POST|PUT|PATCH|DELETE)["']/i.test(src)) {
+ offenders.push(relativePath(file));
+ }
+ }
+ expect(offenders).toEqual([]);
+ });
+});
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────